#####################################################################
# Product:      KnowledgeEngine (Plone Edition)
#
# File:         Permission.py
#
# Copyright:    Copyright (c) 2004, Faulkner Technologies
#
# Author:       Brian Oliver
#
# Description:  A generic Permission instance to represent 
#               an access constraint on a Knowledge Engine object.
#
#               Permissions are stored and managed by implementations
#               of the Knowledge Engine SecurityService.
#
# See Also:     SecurityService
#####################################################################

#####################################################################
## KnowledgeEngine Library Imports
from Common import *

#####################################################################
## Zope Library Imports
from Globals import InitializeClass
from AccessControl import ClassSecurityInfo
from AccessControl.Role import RoleManager
from DateTime import DateTime
 
#####################################################################
## Python Library Imports
# (None)

#the credit management strategies for permissions
CS_NOT_APPLICABLE = "NA"            #not applicable to the type of permission
CS_UNMANAGED = "UNMANAGED"          #credits aren't managed (infinite is available)
CS_MANAGED = "MANAGED"              #the credits are managed (using the credit management service)

#####################################################################
## Permission Class Definition
class Permission(RoleManager):
    """
        A in-memory storage-agnostic representation of a Permission.
    """
        
    #---------------------------------------------------------------#

    #declare security so ClassInfo may be accessed in DTML/ZPT etc
    security = ClassSecurityInfo()
    security.declareObjectPublic()
    security.setDefaultAccess("allow")

    #---------------------------------------------------------------#

    def __init__(self, permissionIdentity, securableIdentity, actorIdentity, isGranted, instantEffectiveFrom = None, instantExpiresOn = None, creditStrategy = CS_NOT_APPLICABLE, assessmentScope = []):
        """
            The default constructor for a Permission
        """
        
        #the persistent attributes
        self.permissionIdentity = permissionIdentity
        self.securableIdentity = securableIdentity
        self.actorIdentity = actorIdentity
        self.isGranted = isGranted
        self.instantEffectiveFrom = instantEffectiveFrom        #a Zope DateTime instance (may be None to indicate no specified time)
        self.instantExpiresOn = instantExpiresOn                #a Zope DateTime instance (may be None to indicate no specified time)
        self.creditStrategy = creditStrategy
        self.assessmentScope = assessmentScope                  #the scope (a list of actorIdentity or *) of assessments to which the permission applies
        
        #transitent attributes (not stored)
        self.lessEffectivePermissions = []  # list of immediately less effective permission
        self.isNew = true
        self.isModified = true
        
    #---------------------------------------------------------------#

    def getIdentity(self):
        """
            Returns the unique identity for the permission
            
            RETURN TYPE: String
        """
        
        return self.permissionIdentity

    #---------------------------------------------------------------#

    def getIsNew(self):
        """
            Returns if the permission state is new (not yet persisted)
            
            RETURN TYPE: Boolean
        """

        return self.isNew

    #---------------------------------------------------------------#

    def setIsNew(self, isNew):
        """
            Sets if the permission state is new (not yet persisted)
        """

        self.isNew = isNew
        
    #---------------------------------------------------------------#

    def getIsModified(self):
        """
            Returns if the permission state has been modified since created or loaded 
        """

        return self.isModified
        
    #---------------------------------------------------------------#

    def setIsModified(self, isModified):
        """
            Sets if the permission state has been modified since created or loaded 
        """

        self.isModified = isModified

    #---------------------------------------------------------------#

    def getPermissionIdentity(self):
        """
            Returns the unique identity for the permission
            
            RETURN TYPE: String
        """
        
        return self.permissionIdentity

    #---------------------------------------------------------------#

    def getSecurableIdentity(self):
        """
            Returns the unique identity for the securable
            
            RETURN TYPE: String
        """
        
        return self.securableIdentity

    #---------------------------------------------------------------#

    def getActorIdentity(self):
        """
            Returns the unique identity for the actor
            
            RETURN TYPE: String
        """
        
        return self.actorIdentity

    #---------------------------------------------------------------#

    def granted(self):
        """
            Returns if the permission is granted.
            
            NOTE: Does not taking into account permission effective dates.
            
            NOTE: Does not take over-riding group or inherited
            permissions into account.  Use a SecurityService
            to do this. eg: hasPermission(...)
            
            The default behaviour is to assume that a permission
            is denied.  
            
            1. If a permission is granted only within
            a date range, then we assume outside that range,
            the permission is denied.
            
            2. If a permission is denied withint a data range,
            outside that date range, we still assume that the 
            permission is denied.  That is, if a permission is denied
            we don't take the effectiveness into consideration.
        """
        
        return self.isGranted

    #---------------------------------------------------------------#

    def grant(self):
        """
            Grants the permission (if within the effective dates)
        """
        
        self.isGranted = true
        self.isModified = true
    
    #---------------------------------------------------------------#

    def denied(self):
        """
            Returns if the permission is denied, taking into account
            the current time of day and the permission effective dates
        """
        
        #proxy the call onto the granted implementation
        return not self.granted()

    #---------------------------------------------------------------#

    def deny(self):
        """
            Denies the permission
        """
        
        self.isGranted = false
        self.isModified = true
        
    #---------------------------------------------------------------#

    def getInstantEffectiveFrom(self):
        """
            Returns the instant in time (as a DateTime) when the permission become effective
            
            RETURN TYPE: DateTime (None indicates no commencement time)
        """

        return self.instantEffectiveFrom

    #---------------------------------------------------------------#

    def setInstantEffectiveFrom(self, instantEffectiveFrom):
        """
            Sets the instant in time (as a DateTime) when the permission becomes effective
            (None indicates not effective)
        """

        self.instantEffectiveFrom = instantEffectiveFrom
        self.isModified = true

    #---------------------------------------------------------------#

    def getInstantExpiresOn(self):
        """
            Returns the instant in time (as a DateTime) when the permission expires
            
            RETURN TYPE: DateTime (None indicates no expiry time)
        """

        return self.instantExpiresOn

    #---------------------------------------------------------------#

    def setInstantExpiresOn(self, instantExpiresOn):
        """
            Sets the instant in time (as a DateTime) when the permission expires
            (None indicates no expiry)
        """

        self.instantExpiresOn = instantExpiresOn
        self.isModified = true

    #---------------------------------------------------------------#

    def getCreditStrategy(self):
        """
            Returns the strategy for managing credits for this permission
            
            RETURN TYPE: String
        """

        return self.creditStrategy

    #---------------------------------------------------------------#

    def setCreditStrategy(self, creditStrategy):
        """
            Sets the strategy for managing credits for this permission
        """

        self.creditStrategy = creditStrategy
        self.isModified = true

    #---------------------------------------------------------------#

    def getAssessmentScope(self):
        """
            Returns a list of actorIdentities (which may include *)
            assessments to which only this permission applies
            (does not accumulate scopes in the permission chain)
            
            NOTE: This method should not be used for determining
            assessment scope. Instead SecurityService.getAccumulatedAssessmentScope()
            should be used as it takes into account chained
            permissions
            
            RETURN TYPE: [String]
        """

        return self.assessmentScope

    #---------------------------------------------------------------#

    def setAssessmentScope(self, assessmentScope):
        """
            Sets the list of actorIdentities (which may include *)
            assessments to which only this permission applies.
        """

        self.assessmentScope = assessmentScope
               
    #---------------------------------------------------------------#

    def getIsApplicable(self):
        """
            Given the current time, is this permission applicable
            taking into account the effective from and expires on times?
        """
        
        #assume applicable
        applicable = true
        
        #get the current time
        now = DateTime()

        #does the permission have an effective from time?
        if self.getInstantEffectiveFrom() is not None:

            #does the permission have an expiries on time?
            if self.getInstantExpiresOn() is not None:
                if now.lessThan(self.getInstantEffectiveFrom()) or now.greaterThan(self.getInstantExpiresOn()):
                    #either not effective yet or it has expired
                    applicable = false

            else:
                #is the permission effective at this moment?
                if now.lessThan(self.getInstantEffectiveFrom()):
                    #not effective yet
                    applicable = false
                
        #does the permission have an expiries on time?
        elif self.getInstantExpiresOn() is not None:
            if now.greaterThan(self.getInstantExpiresOn()):
                #it's expired
                applicable = false                
        
        return applicable
            
    #---------------------------------------------------------------#

    def addLessEffectivePermission(self, lessEffectivePermission):
        """
            Adds the lesser effective permission to this permission
            to form a permission chain. (transient)
            
            This is primarily used to manage the over-all
            'effective credits' available for a chain of permissions.
            
            The chain is built requesting a permission through
            the SecurityService
        """            
        
        self.lessEffectivePermissions = self.lessEffectivePermissions + [lessEffectivePermission]
        
    #---------------------------------------------------------------#

    def getLessEffectivePermissions(self):
        """
            Returns the immediately less effective permissions 
            in the permission chain from this permission. (transient)
        """
        
        return self.lessEffectivePermissions
        
    #---------------------------------------------------------------#

    def toString(self):
        """
            Returns a string representation of the permission
        """
        
        #the persistent attributes
        result = "%sPermission [%s, %s, %s, %d, %s, %s, %s, %s] chain [" % (iif(self.getIsModified(), "*", ""), self.getIdentity(), self.getSecurableIdentity(), self.getActorIdentity(), self.granted(), self.getInstantEffectiveFrom(), self.getInstantExpiresOn(), self.getCreditStrategy(), self.getAssessmentScope())
        
        #add the permissions chain
        for permission in self.getLessEffectivePermissions():
            result = result + ("%s:%s:%s" % (permission.getIdentity(), permission.getSecurableIdentity(), permission.getActorIdentity()))
            
        return result + "]"
        
    #---------------------------------------------------------------#
        
#####################################################################
## Class Initialisation
InitializeClass(Permission)
